package at.tasat;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Random;

import at.tasat.solver.QueueFactory;
import at.tasat.solver.SolverConfiguration;
import at.tasat.solver.queue.DefaultQueueFactory;
import at.tasat.solver.queue.DynamicVariableOccurencesQueueFactory;
import at.tasat.solver.queue.InvertingQueueFactory;
import at.tasat.solver.queue.RandomQueueFactory;
import at.tasat.solver.queue.StaticVariableOccurencesQueueFactory;

public final class Configuration {
	private InputStream in;
	private OutputStream out;
	private OutputStream err;
	private File file;
	private boolean isVerboseTime, isVerboseConfig;
	private Random random;
	private final SolverConfiguration solverConfig;

	{
		in = System.in;
		out = System.out;
		err = System.err;
		file = null;
		isVerboseTime = false;
		isVerboseConfig = false;
		random = new Random();
		solverConfig = new SolverConfiguration();
	}

	public static Configuration get(String[] args) {
		Configuration config = new Configuration();
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
			if (arg.length() == 0) {
				continue;
			}
			if (arg.startsWith("-")) {
				int splitIndex = arg.indexOf('=');
				String name = arg.substring(1, splitIndex);
				String value = arg.substring(splitIndex + 1);
				if (name.equals("v") || name.equals("-verbose")) {
					String[] vvs = value.split(",");
					for (String vv : vvs) {
						if (vv.equals("time")) {
							config.setVerboseTime(true);
						} else if (vv.equals("config")) {
							config.setVerboseConfig(true);
						} else {
							throw new IllegalArgumentException("Unknown verbose mode: " + vv);
						}
					}
				} else if (name.equals("s") || name.equals("-seed")) {
					config.setRandom(new Random(Long.valueOf(value)));
				} else if (name.equals("q") || name.equals("-queue")) {
					QueueFactory factory;
					if (value.equals("default")) {
						factory = new DefaultQueueFactory();
					} else if (value.equals("inverting")) {
						factory = new InvertingQueueFactory();
					} else if (value.equals("random")) {
						factory = new RandomQueueFactory(config.getRandom());
					} else if (value.startsWith("dynamic-occurences")) {
						int threashold = 0;
						if (value.contains(":")) {
							threashold = Integer.valueOf(value.substring(value.indexOf(':') + 1));
						}
						factory = new DynamicVariableOccurencesQueueFactory(threashold);
					} else if (value.equals("static-occurences")) {
						factory = new StaticVariableOccurencesQueueFactory();
					} else {
						throw new IllegalArgumentException("Unknown queue: " + value);
					}
					config.getSolverConfig().setQueueFactory(factory);
				} else if (name.equals("t") || name.equals("-threads")) {
					int nThreads = Integer.parseInt(value);
					if(nThreads < 0){
						throw new IllegalArgumentException("Number of threads must be at least 1!");
					}
					config.solverConfig.setMaxThreads(nThreads);
				} else {
					throw new IllegalArgumentException("Unknown property name: " + name);
				}
			} else {
				config.setIn(new File(arg));
				break;
			}
		}
		return config;
	}

	public InputStream getIn() throws IOException {
		return in != null ? in : (in = new FileInputStream(file));
	}

	public OutputStream getOut() {
		return out;
	}

	public OutputStream getErr() {
		return err;
	}

	public boolean isVerboseTime() {
		return isVerboseTime;
	}

	public boolean isVerboseConfig() {
		return isVerboseConfig;
	}

	public Random getRandom() {
		return random;
	}

	public SolverConfiguration getSolverConfig() {
		return solverConfig;
	}

	public void setIn(InputStream in) {
		assert in != null;
		this.in = in;
		this.file = null;
	}

	public void setIn(File file) {
		assert file != null;
		this.file = file;
		this.in = null;
	}

	public void setOut(OutputStream out) {
		assert out != null;
		this.out = out;
	}

	public void setErr(OutputStream err) {
		assert err != null;
		this.err = err;
	}

	public void setVerboseTime(boolean isVerboseTime) {
		this.isVerboseTime = isVerboseTime;
	}

	public void setVerboseConfig(boolean isVerboseConfig) {
		this.isVerboseConfig = isVerboseConfig;
	}

	public void setRandom(Random random) {
		assert random != null;
		this.random = random;
	}

	@Override
	public String toString() {
		StringBuilder string = new StringBuilder();
		string.append(String.format("--verbose=%s,%s ", String.valueOf(isVerboseTime ? "time" : ""), String.valueOf(isVerboseConfig ? "config" : "")));
		string.append(String.format("--queue=%s ", solverConfig.getQueueFactory().getName()));
		if (file != null) {
			string.append(file.getAbsolutePath());
		}
		return string.toString().trim();
	}
}
